﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System;
using System.Net;
using System.IO;
using System.Runtime.Serialization;
using System.Threading;
using System.ComponentModel;
using System.Collections;
using BrainTechLLC.Communication;
using System.Collections.Generic;
using BrainTechLLC.ThreadSafeObjects;
using AltSerialize;
using System.Reflection;
#if NO_SILVERLIGHT
using BrainTechLLC.Logging;
using System.Text;
using Mud.EmlenMudMain;
using System.Diagnostics;
#endif
#if USE_HYPER
using Hyper.ComponentModel;
#endif

namespace BrainTechLLC
{
    public class MapIDToControlReceiver : IMapIDToControlReceiver
    {
        #region IMapIDToControlReceiver Members

        public object FindReceiver(long ID)
        {
            return Everything.Find<IGameThing>(ID);
        }

        #endregion
    }

    public class LockList
    {
        public static readonly Dictionary<long, ThreadSafeCounter> CurrentlyLocked = new Dictionary<long, ThreadSafeCounter>();
        public static readonly object _lockObject = new object();

        protected List<IHasDependencies> _listToLock;
        public List<IHasDependencies> ListToLock
        {
            get
            {
                if (_listToLock == null)
                    Interlocked.CompareExchange<List<IHasDependencies>>(ref _listToLock, new List<IHasDependencies>(), null);

                return _listToLock;
            }
            set
            {
                _listToLock = value;
            }
        }

        public void DetermineDependencies(IHasDependencies item, ref Dictionary<long, IHasDependencies> outputDependencyList)
        {
            if (outputDependencyList == null)
                outputDependencyList = new Dictionary<long, IHasDependencies>();

            if (!outputDependencyList.ContainsKey(item.AssociatedID))
                outputDependencyList.Add(item.AssociatedID, item);

            if (item.Dependencies == null || item.Dependencies.Count == 0)
                return;

            var kvps = item.Dependencies.AllKeyValuePairs;

            foreach (var kvp in kvps)
            {
                IHasDependencies child = kvp.Value as IHasDependencies;

                if (kvp.Value == null)
                {
                    var o = Everything.Find<IGameThing>(kvp.Key);
                    item.Dependencies.AddOrSet(kvp.Key, o);
                    child = o as IHasDependencies;
                }

                if (child != null && !outputDependencyList.ContainsKey(child.AssociatedID))
                {
                    //outputDependencyList.Add(child.AssociatedID, child);
                    DetermineDependencies(child, ref outputDependencyList);
                }
            }
        }

        public bool CanAquireLock(List<KeyValuePair<long, IAssociatedID>> kvps)
        {
            lock (_lockObject)
            {
                foreach (var kvp in kvps)
                {
                    if (CurrentlyLocked.ContainsKey(kvp.Key))
                        return false;
                }
            }

            return true;
        }

        public bool CanAquireLock()
        {
            return CanAquireLock(AllLockRequirements.AllKeyValuePairs);
        }

        public void ReleaseLock()
        {
            var kvps = AllLockRequirements.AllKeyValuePairs;

            lock (_lockObject)
            {
                foreach (var kvp in kvps)
                {
                    ThreadSafeCounter counter = CurrentlyLocked[kvp.Key];
                    if (counter.Count == 1)
                    {
                        CurrentlyLocked.Remove(kvp.Key);
                    }
                    else
                    {
                        counter.Decrement();
                    }
                }
            }
        }

        public bool TryAquireLock()
        {
            var kvps = AllLockRequirements.AllKeyValuePairs;

            lock (_lockObject)
            {
                if (CanAquireLock(kvps))
                {
                    foreach (var kvp in kvps)
                    {
                        ThreadSafeCounter counter;
                        if (!CurrentlyLocked.TryGetValue(kvp.Key, out counter))
                        {
                            counter = new ThreadSafeCounter(1);
                            CurrentlyLocked.Add(kvp.Key, counter);
                        }
                        else
                        {
                            counter.Increment();
                        }
                    }

                    return true;
                }
            }
            return false;
        }

        public ThreadSafeLookup<long, IAssociatedID> AllLockRequirements
        {
            get
            {
                ThreadSafeLookup<long, IAssociatedID> result = new ThreadSafeLookup<long, IAssociatedID>();
                Dictionary<long, IHasDependencies> dependencyDictionary = null;

                if (_listToLock == null)
                    return result;


                foreach (IHasDependencies item in ListToLock)
                {
                    DetermineDependencies(item, ref dependencyDictionary);
                }

                if (dependencyDictionary != null)
                {
                    foreach (var kvp in dependencyDictionary)
                    {
                        result.AddOrSet(kvp.Key, kvp.Value);
                    }
                }

                //result = result.Merge(item.Dependencies);

                return result;
            }
        }
    }

    public static class Core_RegisterTypes
    {
        public static Type[] RegisterTypes()
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            DLLContents contents = TypeProvider.GetInstance().GetDLLContents(assembly.FullName);
            var types = assembly.GetTypes();
            types.ForEach(type =>
            {
                if (type.IsTypeOkay())
                    TypeProvider.GetInstance().AddType(contents, type);
            });
            return types;
        }
    }

    public static class AltSerializeHelper
    {
        public static bool Initialized = false;
        public static object _lockObj = new object();

        public static void CheckInit(IEnumerable<Type> types)
        {
            if (!Initialized)
            {
                lock (_lockObj)
                {
                    if (!Initialized)
                    {
                        //Serializer.DefaultSerializeFlags = SerializeFlags.None; // SerializeFlags.SerializationCache; // SerializeFlags.None;// SerializeFlags.SerializationCache; // SerializeFlags.None; // SerializeFlags.SerializationCache;// | SerializeFlags.SerializePropertyNames; // SerializeFlags.None;// SerializeFlags.SerializationCache | SerializeFlags.SerializePropertyNames;                        

#if USE_ADDITIONAL_TYPES
                        List<Type> serTypes = new List<Type>(types);
                        serTypes.AddRange(IncomingMessageHandler.KnownTypesArray);

                        AltSerializer.AddAdditionalTypes(serTypes);
#endif

                        //foreach (var type in IncomingMessageHandler.KnownTypesArray)
                        //    Serializer.CacheObject(type);

                        Initialized = true;
                    }
                }
            }
        }

        public static byte[] Serialize(this object o)
        {
            CheckInit(TypeProvider._typesForSerialize.AllItems);
            Serializer s = new Serializer();
            foreach (var type in IncomingMessageHandler.KnownTypesArray)
                s.CacheObject(type);
            byte[] result = s.Serialize(o);
            return result;
        }

        public static byte[] Serialize(this object o, Type t)
        {
            CheckInit(TypeProvider._typesForSerialize.AllItems);
            Serializer s = new Serializer();
            foreach (var type in IncomingMessageHandler.KnownTypesArray)
                s.CacheObject(type);
            byte[] result = s.Serialize(o, t);
            return result;
        }
    }

    public class IncomingMessageHandler : IIncomingMessageHandler
    {
        public static Type[] KnownTypesArray = new Type[] { 
            typeof(AssociatedIDCollection), 
            typeof(InfluenceCollection),
            typeof(GameThingBaseClass), 
            typeof(BaseClass), 
            typeof(LocationInt), 
            typeof(GameChar), 
            typeof(GameLocation), 
            typeof(GameCharTemplate), 
            typeof(Template), 
            typeof(CombatSettings),             
            typeof(CharPosition), 
            typeof(Sex), 
            typeof(TempStatusFlags), 
            typeof(GeneralDirection) ,
            typeof(ThreadSafeLookup<string, IdentifierAndValue>),
            typeof(ThreadSafeLookupNonRef<long, long>),
            typeof(IdentifierAndValue),
            typeof(Dictionary<long, long>),            
            typeof(List<NameAndDescription>),
            typeof(Dictionary<string, IdentifierAndValue>),
            typeof(MobTypes),
            typeof(ActionsAndBehavior),            
        };

        #region IIncomingMessageHandler Members

        public void HandleIncomingMessage(ICommIncomingConnection connection, ICommunicationsMessage message)
        {
            switch ((TransmissionEnum)message.MessageType)
            {
                case TransmissionEnum.RequestUniqueId:
                    NumericMessage msg = new NumericMessage() { Numeric = (connection as IncomingConnection).UniqueId };
                    connection.SendMessage(msg, (int)TransmissionEnum.RespondUniqueId);
                    break;

                case TransmissionEnum.TwoStringsAndTwoNumerics:
                    // HEREHERE
                    break;

#if NO_SILVERLIGHT
                case TransmissionEnum.RequestAllGameChars:
                    HandleRequestGameChars(connection);
                    break;

                case TransmissionEnum.RequestAllGameCharTemplates:
                    HandleRequestGameCharTemplates(connection);
                    break;

                case TransmissionEnum.RequestAllLocations:
                    HandleRequestAllLocations(connection);
                    break;

                case TransmissionEnum.RequestAllItemTemplates:
                    HandleRequestAllItemTemplates(connection);
                    break;

                case TransmissionEnum.RequestAllItems:
                    HandleRequestAllItems(connection);
                    break;

                case TransmissionEnum.RequestAllLocationEdges:
                    HandleRequestAllLocationEdges(connection);
                    break;

                case TransmissionEnum.RequestAllAreas:
                    HandleRequestAllAreas(connection);
                    break;

                case TransmissionEnum.RequestAllResets:
                    HandleRequestAllResets(connection);
                    break;

                case TransmissionEnum.RequestObjectById:
                    RequestObjectById req = message.MessageContents as RequestObjectById;
                    HandleRequestObjectById(connection, req.ID);
                    break;

#endif
            }

            // Console.WriteLine(message.MessageContents.ToString());
        }

        #endregion

#if NO_SILVERLIGHT
        protected void HandleRequestAllLocations(ICommIncomingConnection connection)
        {
            ByteDataMessage byteMessage = new ByteDataMessage();
            List<GameLocation> locations = new List<GameLocation>(WorldMap.Instance.World.AllItems);
            try
            {
                byte[] bytes = locations.Serialize(typeof(List<GameLocation>));
                byteMessage.Bytes = bytes.Compress();
                byteMessage.ByteCount = byteMessage.Bytes.Length;
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.ToString());
            }
            connection.SendMessage(byteMessage, (int)TransmissionEnum.RequestAllLocations);
        }

        public static byte[] HandleRequestAll<T>() where T : class, IGameThing
        {
            byte[] bytes = null;
            List<T> templates = Everything.FindAllThingsOfType<T>();
            try
            {
                bytes = templates.Serialize(typeof(List<T>));
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.ToString());
            }
            return bytes;
        }

        public static byte[] HandleRequestSingle(long id) 
        {
            byte[] bytes = null;
            var item = id.Find();
            try
            {
                bytes = item.Serialize(); //HEREHERE item.GetType());
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.ToString());
            }
            return bytes;
        }

        public static void HandleRequestAll<T>(ICommIncomingConnection connection, TransmissionEnum transType) where T : class, IGameThing
        {
            ByteDataMessage byteMessage = new ByteDataMessage();
            byte[] bytes = HandleRequestAll<T>();
            
            if (bytes != null)
            {
                byteMessage.Bytes = bytes.Compress();
                byteMessage.ByteCount = byteMessage.Bytes.Length;
                connection.SendMessage(byteMessage, (int)transType);
            }            
        }

        public static void HandleRequestSingle(ICommIncomingConnection connection, long id, TransmissionEnum transType) 
        {
            ByteDataMessage byteMessage = new ByteDataMessage();
            byte[] bytes = HandleRequestSingle(id);

            if (bytes != null)
            {
                byteMessage.Bytes = bytes.Compress();
                byteMessage.ByteCount = byteMessage.Bytes.Length;
                connection.SendMessage(byteMessage, (int)transType);
            }
        }

        protected void HandleRequestGameCharTemplates(ICommIncomingConnection connection)
        {
            HandleRequestAll<GameCharTemplate>(connection, TransmissionEnum.RequestAllGameCharTemplates);
        }

        protected void HandleRequestAllItemTemplates(ICommIncomingConnection connection)
        {
            HandleRequestAll<ItemTemplate>(connection, TransmissionEnum.RequestAllItemTemplates);
        }

        protected void HandleRequestAllItems(ICommIncomingConnection connection)
        {
            HandleRequestAll<Item>(connection, TransmissionEnum.RequestAllItems);
        }

        protected void HandleRequestAllLocationEdges(ICommIncomingConnection connection)
        {
            HandleRequestAll<LocationEdge>(connection, TransmissionEnum.RequestAllLocationEdges);
        }

        protected void HandleRequestAllAreas(ICommIncomingConnection connection)
        {
            HandleRequestAll<Area>(connection, TransmissionEnum.RequestAllAreas);
        }

        protected void HandleRequestAllResets(ICommIncomingConnection connection)
        {
            HandleRequestAll<Reset>(connection, TransmissionEnum.RequestAllResets);
        }

        protected void HandleRequestObjectById(ICommIncomingConnection connection, long id)
        {
            HandleRequestSingle(connection, id, TransmissionEnum.RequestObjectById);            
        }

        protected void HandleRequestGameChars(ICommIncomingConnection connection)
        {
            HandleRequestAll<GameChar>(connection, TransmissionEnum.RequestAllGameChars);
        }
#endif
    }

    [Serializable]
    [DataContract]
    public class BytesAndType
    {
        [DataMember]
        public string Bytes { get; set; }
        [DataMember]
        public string Type { get; set; }
    }
}